package com.juitar.base;

/**
 * @author SKopylov
 */
public class Fingering extends SimpleFingering {

    private final Chord chord;
    private final Tuning tuning;

    public Fingering(int[] fingering, Chord chord, Tuning tuning) {
        super(fingering);
        this.chord = chord;
        this.tuning = tuning;
    }

    public boolean isChord() {
        int[] open = tuning.getOpenStringNotes();
        int mask = 0;
        for (int i = 0; i < fingering.length; i++) {
            if (fingering[i] >= 0) {
                int current = open[i] + fingering[i];
                int contains = chord.contains(current);
                if (contains > 0) {
                    mask |= 1 << (contains - 1);
                }
            }
        }
        int n = chord.numOfNotes();
        switch (n) {
        case 3:
            // 0111
            return mask == 0x07;
        case 4:
            // 1111 or 1011
            return mask == 0x0F || mask == 0x0B;
        case 5:
            // 11111 or 11011
            // fifth step could be omitted
            return mask == 0x1F || mask == 0x1B;
        default:
            throw new IllegalArgumentException("Too complex chord " + chord);
        }
    }

    /**
     * @return chord inversion
     */
    public int getInversion() {
        int[] open = tuning.getOpenStringNotes();
        int i = fingering.length;
        while (--i >= 0) {
            if (fingering[i] >= 0) {
                int current = open[i] + fingering[i];
                return chord.contains(current);
            }
        }
        throw new RuntimeException("Could not find inversion");
    }

    public int getBassRootString() {
        int[] open = tuning.getOpenStringNotes();
        int i = fingering.length;
        while (--i >= 0) {
            if (fingering[i] >= 0) {
                int current = open[i] + fingering[i];
                if (chord.contains(current) == 1) { return i; }
            }
        }
        throw new RuntimeException("Could not find bass root string for chord:" + chord
                                   + ", tuning:"
                                   + tuning
                                   + " ,fingering:"
                                   + this);
    }

    /**
     * Gets full chord name with bass (for example Cmaj/E) 
     * @return full chord name with bass (for inversion > 1)
     */
    public String chordName() {
        String name = chord.getChordName();
        int inv = getInversion();
        if (inv != 1) {
            int note = chord.getRootNote() + chord.getNotes()[inv-1];
            name += "/" + Note.toString(note);
        }
        return name;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) { return true; }
        // TODO What about chord and tuning?
        return super.equals(obj);
    }

    @Override
    public int hashCode() {
        return super.hashCode();
    }

    public static final Fingering C_MAJOR = new Fingering(new int[] { 0, 1, 0, 2, 3, -1 },
            Chord.MAJOR, Tuning.STANDARD);
}
